home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / newsgate / news2mail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-07-18  |  13.3 KB  |  545 lines

  1. /*
  2. **  NEWS2MAIL
  3. **  Read a news article on standard input, and send it to the mailing
  4. **  list as directed by the command-line arguments.  It does some
  5. **  parsing and converting of news headers into mail headers.
  6. **
  7. **  This program wants to lie to sendmail, so it should be setuid to
  8. **  one of the "trusted" users as listed in your sendmail.cf file.
  9. **
  10. */
  11. #include "gate.h"
  12. #include <sys/stat.h>
  13. #if    defined(RCSID)
  14. static char RCS[] =
  15.     "$Header: /nfs/papaya/u2/rsalz/src/newsgate/RCS/news2mail.c,v 1.14 91/07/18 21:14:26 rsalz Exp $";
  16. #endif    /* defined(RCSID) */
  17.  
  18.  
  19. /* Flags for special header lines. */
  20. typedef enum _HEADERTYPE {
  21.     HDR_NORM,
  22.     HDR_SUBJ,
  23.     HDR_CTRL,
  24.     HDR_REFS,
  25.     HDR_PATH,
  26.     HDR_FROM
  27. } HEADERTYPE;
  28.  
  29.  
  30. /* Header-cracking datatype. */
  31. typedef struct _HEADER {
  32.     char        *Tag;
  33.     int            Length;
  34.     HEADERTYPE        Flag;
  35.     char        Value[SM_SIZE];
  36. } HEADER;
  37.  
  38. STATIC int    Debugging;
  39. char        *Pname;
  40.  
  41. /* The headers we care about. */
  42. STATIC HEADER    Table[] = {
  43.     {    "Control",     7,     HDR_CTRL    },
  44.     {    "Date",         4,     HDR_NORM    },
  45.     {    "From",         4,     HDR_FROM    },
  46.     {    "Message-ID",    10,     HDR_NORM    },
  47.     {    "Organization",    12,     HDR_NORM    },
  48.     {    "Path",         4,     HDR_PATH    },
  49.     {    "References",    10,     HDR_REFS    },
  50.     {    "Reply-To",     8,     HDR_NORM    },
  51.     {    "Subject",     7,     HDR_SUBJ    },
  52. };
  53.  
  54.  
  55. #if    defined(USE_PATH_FOR_FROM)
  56. /*
  57. **  Figure out the return address, by doing some optimization on the
  58. **  path.  If we find an Internet host or a UUCP neighbor, remove all
  59. **  hosts before that one from the path.
  60. */
  61. STATIC char *
  62. EditPath(path, Host)
  63.     register char    *path;
  64.     char        *Host;
  65. {
  66.     static char        **Hinet;
  67.     static char        **Huucp;
  68.     static char        **Lsys;
  69.     static char        buff[SM_SIZE];
  70.     register char    *p;
  71.     register char    *q;
  72.     register char    **V;
  73.     register int    ac;
  74.     register int    i;
  75.     register int    uucppal;
  76.     register int    inetpal;
  77.     FILE        *F;
  78.     FILE        *P;
  79.     struct stat        Sb1;
  80.     struct stat        Sb2;
  81.     char        **av;
  82.     char        *Inetsite;
  83.  
  84.     if ((ac = Split(path, &av, '!')) == 2) {
  85.     /* Simple case:  "site!user" */
  86.     Strcpy(buff, av[1]);
  87.     SplitFree(&av);
  88.     return buff;
  89.     }
  90.  
  91.     /* Initialize.  This is silly for now, but eventually we might want
  92.      * to be able to handle a batched feed. */
  93. #if    defined(UUNAME)
  94.     if (Lsys == NULL) {
  95.     p = UUNAME;
  96.     /* If someone diddled L.sys, rebuild uuname output. */
  97.     if (stat(p, &Sb2) < 0
  98. #if    defined(L_SYS)
  99.      || stat(L_SYS, &Sb1) < 0
  100. #endif    /* defined(L_SYS) */
  101.      || Sb1.st_mtime >= Sb2.st_mtime)
  102.         if ((F = fopen(p, "w"))  == NULL)
  103.         Fprintf(stderr, "%s:  Can't create %s, %s.\n",
  104.             Pname, p, strerror(errno));
  105.         else {
  106.         if ((P = popen("exec uuname", "r")) == NULL)
  107.             Fprintf(stderr, "%s:  popen failed, %s.\n",
  108.                 Pname, strerror(errno));
  109.         else {
  110.             while (fgets(buff, sizeof buff, P))
  111.             Fputs(buff, F);
  112.             if (pclose(P))
  113.             Fprintf(stderr, "%s:  pclose failed, %s.\n",
  114.                 Pname, strerror(errno));
  115.         }
  116.         if (fclose(F) == EOF)
  117.             Fprintf(stderr, "%s:  Error closing %s, %s.\n",
  118.                 Pname, p, strerror(errno));
  119.         }
  120.  
  121.     /* Slurp up names of UUCP hosts we talk to. */
  122.     Lsys = ReadFile(p);
  123.  
  124. #if    defined(UUCP_INET)
  125.     /* Slurp up the UUCP->Internet name mappings. */
  126.     for (Huucp = ReadFile(UUCP_INET), i = 1; Huucp[i]; i++)
  127.         continue;
  128.     for (Hinet = NEW(char*, i), i = 0; (p = Huucp[i]) != NULL; i++) {
  129.         while (*p && !WHITE(*p))
  130.         p++;
  131.         if (*p)
  132.         for (*p++ = '\0'; *p && WHITE(*p); p++)
  133.             continue;
  134.         Hinet[i] = p;
  135.     }
  136. #endif    /* defined(UUCP_INET) */
  137.     }
  138. #endif    /* defined(UUNAME) */
  139.  
  140.     /* Scan the path, noting if we find a UUCP or Internet neighbor. */
  141.     for (uucppal = 1, inetpal = 0, i = 0; i < ac; i++) {
  142.     if (Lsys)
  143.         for (V = Lsys; *V; V++)
  144.         if (EQ(av[i], *V))
  145.             uucppal = i;
  146.     if (Huucp)
  147.         for (V = Huucp; *V; V++)
  148.         if (EQ(av[i], *V)) {
  149.             inetpal = i;
  150.             Inetsite = Hinet[V - Huucp];
  151.         }
  152.     }
  153.  
  154.     if (inetpal < uucppal) {
  155.     /* No Internet site found, turn a!b!c into a!b!c@this-host */
  156.     for (p = buff + APPEND(buff, av[uucppal]); ++uucppal < ac; ) {
  157.         *p++ = '!';
  158.         p += APPEND(p, av[uucppal]);
  159.     }
  160.     *p++ = '@';
  161.     Strcpy(p, Host);
  162.     }
  163.     else if (inetpal == ac - 1)
  164.     /* Turn a!b!inet!user into user@inet.domain.name */
  165.     Sprintf(buff, "%s@%s", av[ac], Inetsite);
  166.     else {
  167.     /* Turn a!inet!b!user into b!user@inet.domain.name */
  168.     for (p = buff + APPEND(buff, av[++inetpal]); ++inetpal < ac; ) {
  169.         *p++ = '!';
  170.         p += APPEND(p, av[inetpal]);
  171.     }
  172.     *p++ = '@';
  173.     Strcpy(p, Inetsite);
  174.     }
  175.  
  176.     /* Convert all but the last "@" to "%" (fie on decwrl and psuecl!). */
  177.     if ((p = IDX(buff, '@')) != NULL)
  178.     for ( ; (q = IDX(p + 1, '@')) != NULL; p = q)
  179.         *p = '%';
  180.  
  181.     SplitFree(&av);
  182.     return buff;
  183. }
  184. #endif    /* defined(USE_PATH_FOR_FROM) */
  185.  
  186.  
  187. /*
  188. **  Hack up the references, taking only the last three.
  189. */
  190. STATIC char *
  191. TrimReferences(refs)
  192.     char         *refs;
  193. {
  194.     static char        buff[SM_SIZE];
  195.     register char    *p;
  196.     register int    i;
  197.     register int    ac;
  198.     char        **av;
  199.  
  200.     if ((ac = Split(refs, &av, '\0')) != 0) {
  201.     /* Tricky.  If there are five references, we want subscripts 2,3,4. */
  202.     i = ac < 3 ? 0 : ac - 3;
  203.     for (p = buff + APPEND(buff, av[i]); ++i < ac; ) {
  204.         *p++ = ',';
  205.         *p++ = ' ';
  206.         p += APPEND(p, av[i]);
  207.     }
  208.     SplitFree(&av);
  209.     }
  210.     else
  211.     buff[0] = '\0';
  212.     return buff;
  213. }
  214.  
  215.  
  216. #if    !defined(HAVE_PUTENV)
  217. /*
  218. **  A brute-forced implementation of putenv.  Wastes memory.  Consider
  219. **  it incentive to install the free BSD version...
  220. */
  221. int
  222. putenv(val)
  223.     char    *val;
  224. {
  225.     char    **new;
  226.     int        i;
  227.     int        length;
  228.     int        found;
  229.     char    *p;
  230.  
  231.     /* See if the value is already in the environment. */
  232.     found = -1;
  233.     if (p = IDX(val, '=')) {
  234.     for (length = ++p - val, i = 0; environ[i]; i++)
  235.         if (EQn(val, environ[i], length)) {
  236.         found = i;
  237.         break;
  238.         }
  239.     }
  240.  
  241.     /* Get the size, and space for the new environment. */
  242.     for (i = 0; environ[i]; i++)
  243.     ;
  244.     i += 2;
  245.     new = NEW(char*, i);
  246.     new[0] = val;
  247.  
  248.     /* Copy the old to the new. */
  249.     for (i = 0; environ[i]; i++)
  250.     if (i != found)
  251.         new[i + 1] = environ[i];
  252.     new[i + 1] = NULL;
  253.     environ = new;
  254.     return 0;
  255. }
  256. #endif    /* !defined(HAVE_PUTENV) */
  257.  
  258.  
  259. /*
  260. **  Print a usage message and exit.
  261. */
  262. STATIC void
  263. Usage()
  264. {
  265.     Fprintf(stderr, "Usage:\n\t%s %s %s\n",
  266.     Pname,
  267.     "[-.] [-e var=val]",
  268.     "listname listaddr listadmin host [article]");
  269.     exit(EX_USAGE);
  270. }
  271.  
  272.  
  273. main(ac, av)
  274.     int            ac;
  275.     register char    *av[];
  276. {
  277.     static char        tmp[sizeof TEMPFILE];
  278.     register FILE    *F;
  279.     register HEADER    *hp;
  280.     register char    *p;
  281.     char        *sv[10];
  282.     char        buff[BUFSIZ];
  283.     char        SenderAddr[SM_SIZE];
  284.     char        ToAddr[SM_SIZE];
  285.     char        Host[SM_SIZE];
  286. #if    defined(USE_PATH_FOR_FROM)
  287.     char        Fullname[SM_SIZE];
  288. #endif    /* defined(USE_PATH_FOR_FROM) */
  289.     int            i;
  290.     int            HadEflag;
  291.     char        *Listname;
  292.     char        *Listaddr;
  293.     char        *Listadmin;
  294.     char        *Listhost;
  295.     char        *Article;
  296.  
  297.     /* Set defaults. */
  298.     if ((Pname = RDX(av[0], '/')) == NULL)
  299.     Pname = av[0];
  300.     else
  301.     Pname++;
  302.     HadEflag = FALSE;
  303.  
  304.     /* Parse JCL. */
  305.     while ((i = getopt(ac, av, "E:.")) != EOF)
  306.     switch (i) {
  307.     default:
  308.         Usage();
  309.         /* NOTREACHED */
  310.     case '.':
  311.         Debugging = TRUE;
  312.         break;
  313.     case 'E':
  314.         if (putenv(COPY(optarg))) {
  315.         Fprintf(stderr, "%s:  Can't add to environment, %s.\n",
  316.             Pname, strerror(errno));
  317.         exit(EX_TEMPFAIL);
  318.         }
  319.         HadEflag = TRUE;
  320.         break;
  321.     }
  322.     ac -= optind;
  323.     av += optind;
  324.     if (ac != 4 && ac != 5)
  325.     Usage();
  326.  
  327.     /* Parse the positional parameters. */
  328.     Listname = av[0];
  329.     Listaddr = av[1];
  330.     Listadmin = av[2];
  331.     Listhost = av[3];
  332.     Article = av[4];
  333.  
  334.     /* Arrange for logging. */
  335.     if (!Debugging && freopen(ERR_LOG, "a", stderr) == NULL)
  336.     /* Sigh; error in error handler.... */
  337.     (void)freopen("/dev/console", "w", stderr);
  338.  
  339.     if (Article && freopen(Article, "r", stdin) == NULL) {
  340.     Fprintf(stderr, "%s:  Can't open %s, %s.\n",
  341.         Pname, Article, strerror(errno));
  342.     exit(EX_NOINPUT);
  343.     }
  344.  
  345.     /* Who are we? */
  346. #if    defined(WHOAMI)
  347.     Strcpy(Host, WHOAMI);
  348. #else
  349.     if (gethostname(Host, sizeof Host) < 0) {
  350.     Fprintf(stderr, "%s:  Can't get hostname, %s.\n",
  351.         Pname, strerror(errno));
  352.     exit(EX_TEMPFAIL);
  353.     }
  354. #endif    /* defined(WHOAMI) */
  355.  
  356.     /* Read headers, storing the ones we want. */
  357.     while (fgets(buff, sizeof buff, stdin)) {
  358.     if ((p = IDX(buff, '\n')) != NULL)
  359.         *p = '\0';
  360.     else
  361.         Fprintf(stderr, "%s:  Header line too long (%d bytes max)\n\t%s\n",
  362.         Pname, sizeof buff, buff);
  363.     if (p == buff)
  364.         /* Blank line means end of headers. */
  365.         break;
  366.  
  367.     for (hp = Table; hp < ENDOF(Table); hp++)
  368.         if (buff[hp->Length] == ':' && EQn(hp->Tag, buff, hp->Length)) {
  369.         /* Skip whitespace. */
  370.         for (p = &buff[hp->Length + 1]; *p && WHITE(*p); p++)
  371.             continue;
  372.         switch (hp->Flag) {
  373.         case HDR_SUBJ:
  374.             if (!EQn(p, "cmsg ", 5)) {
  375.             Strcpy(hp->Value, p);
  376.             break;
  377.             }
  378.             /* FALLTHROUGH */
  379.         case HDR_CTRL:
  380.             /* Eat rest of message. */
  381.             while (fgets(buff, sizeof buff, stdin))
  382.             continue;
  383.             exit(EX_OK);
  384.             /* NOTREACHED */
  385.         case HDR_NORM:
  386.             Strcpy(hp->Value, p);
  387.             break;
  388.         case HDR_FROM:
  389. #if    defined(USE_PATH_FOR_FROM)
  390.             /* Turn "joe@site.uucp (My Name)" into "(My Name)" */
  391.             if ((p = IDX(p, ' ')) && p[1] == '(')
  392.             Strcpy(Fullname, ++p);
  393.             else
  394.             Fullname[0] = '\0';
  395. #else
  396.             Strcpy(hp->Value, p);
  397. #endif    /* defined(USE_PATH_FOR_FROM) */
  398.             break;
  399.         case HDR_REFS:
  400.             Strcpy(hp->Value, TrimReferences(p));
  401.             break;
  402. #if    defined(USE_PATH_FOR_FROM)
  403.         case HDR_PATH:
  404.             Strcpy(hp->Value, EditPath(p, Host));
  405.             break;
  406. #endif    /* defined(USE_PATH_FOR_FROM) */
  407.         }
  408.     }
  409.     }
  410.  
  411.     /* Set up temp output. */
  412.     if ((F = fopen(mktemp(strcpy(tmp, TEMPFILE)), "w")) == NULL) {
  413.     Fprintf(stderr, "%s:  Can't create %s, %s.\n", tmp, strerror(errno));
  414.     exit(EX_CANTCREAT);
  415.     }
  416.  
  417.     if (IDX(Listadmin, '@'))
  418.     Strcpy(SenderAddr, Listadmin);
  419.     else
  420.     Sprintf(SenderAddr, "%s@%s", Listadmin, Listhost);
  421.     if (IDX(Listaddr, '@'))
  422.     Strcpy(ToAddr, Listaddr);
  423.     else
  424.     Sprintf(ToAddr, "%s@%s", Listaddr, Listhost);
  425.  
  426.     /* Print out a sanitized header for mail. */
  427.     if (IDX(Listname, '@'))
  428.     Strcpy(buff, Listname);
  429.     else
  430.     Sprintf(buff, "%s@%s", Listname, Listhost);
  431.     Fprintf(F, "Received: from GATEWAY by %s with netnews\n", Host);
  432.     Fprintf(F, "\tfor %s (%s)\n", ToAddr, buff);
  433.     Fprintf(F, "To: %s\n", buff);
  434.     for (hp = Table; hp < ENDOF(Table); hp++)
  435.     if (hp->Flag == HDR_PATH) {
  436. #if    defined(USE_PATH_FOR_FROM)
  437.         Fprintf(F, "From: %s %s\n", hp->Value, Fullname);
  438. #endif    /* defined(USE_PATH_FOR_FROM) */
  439.         Fprintf(F, "Sender: %s\n", SenderAddr);
  440.     }
  441.     else if (hp->Value[0])
  442.         Fprintf(F, "%s: %s\n", hp->Tag, hp->Value);
  443.     Fprintf(F, "\n");
  444.  
  445.     /* Dump the body of the message. */
  446.     while (fgets(buff, sizeof buff, stdin))
  447.     Fputs(buff, F);
  448.     if (fclose(F) == EOF)
  449.     Fprintf(stderr, "%s:  Error closing %s, %s.\n",
  450.         Pname, tmp, strerror(errno));
  451.  
  452.     /* Set I/O correctly.  Stderr is going to the log, stdin should be the
  453.      * message we created.  Easiest thing is to unlink an open file. */
  454.     if (freopen(tmp, "r", stdin) == NULL) {
  455.     Fprintf(stderr, "%s:  Can't open %s for reading, %s.\n",
  456.         Pname, tmp, strerror(errno));
  457.     exit(EX_OSERR);
  458.     }
  459.     if (unlink(tmp) < 0)
  460.     Fprintf(stderr, "%s:  Can't unlink %s, %s.\n",
  461.         Pname, tmp, strerror(errno));
  462.  
  463.     /* Common code for all argument vectors. */
  464.     i = 0;
  465.  
  466. #if    defined(MAILSCRIPT)
  467.     /* Build the MAILSCRIPT argument vector. */
  468.     sv[i++] = MAILSCRIPT;
  469.     /* Headers are inline. */
  470.     sv[i++] = "-ASIS";
  471.     /* Set the logical sender/from address. */
  472.     sv[i++] = "-From";
  473.     sv[i++] = SenderAddr;
  474.     /* Set the recipient. */
  475.     sv[i++] = "-recip";
  476.     sv[i++] = ToAddr;
  477. #endif    /* defined(MAILSCRIPT) */
  478.  
  479. #if    defined(SENDMAIL)
  480.     /* Build of the SENDMAIL argument vector. */
  481.     sv[i++] = SENDMAIL;
  482.     /* Ignore periods as message terminator (same as -oi). */
  483.     sv[i++] = "-i";
  484.     /* Queued delivery. */
  485.     sv[i++] = "-odq";
  486.     /* Set the "From:" address. */
  487.     sv[i++] = "-f";
  488.     sv[i++] = SenderAddr;
  489.     /* Set the recipient. */
  490.     sv[i++] = ToAddr;
  491. #endif    /* defined(SENDMAIL) */
  492.  
  493. #if    defined(MMDF)
  494.     /* Build of the MMDF argument vector. */
  495.     sv[i++] = MMDF;
  496.     /* Deliver to mailbox (m), deliver all mail now (ln), trust me (t), send
  497.      * no warnings (z), return to "Sender:" (s), get recipients from the
  498.      * "To:" address (xto*). */
  499. #if    defined(MMDF_DELIVER_NOW)
  500.     sv[i++] = "-mlntzsxto*";
  501. #else
  502.     sv[i++] = "-mtzsxto*";
  503. #endif    /* defined(MMDF_DELIVER_NOW) */
  504.     /* Set the From: address. */
  505.     sv[i++] = SenderAddr;
  506. #endif    /* defined(MMDF) */
  507.  
  508.     /* Null-terminate the vector. */
  509.     sv[i] = NULL;
  510.  
  511.     if (Debugging) {
  512.     for (i = 0; sv[i]; i++)
  513.         (void)printf(" |%s| ", sv[i]);
  514.     (void)printf("\n");
  515.     if (HadEflag) {
  516.         for (i = 0; sv[i]; i++)
  517.         (void)printf(" [%s] ", sv[i]);
  518.         (void)printf("\n");
  519.     }
  520.     while (fgets(buff, sizeof buff, stdin))
  521.         Fputs(buff, stdout);
  522.     exit(EX_OK);
  523.     }
  524.  
  525.     /* Try to setuid, if desired.  Could "factor out" the execv from the
  526.      * #if, but I hate the way the resultant "dangling else" looks. */
  527. #if    defined(TRUSTED)
  528.     if (setuid(TRUSTED) < 0)
  529.     Fprintf(stderr, "%s:  Can't setuid to %d, %s.\n",
  530.         Pname, TRUSTED, strerror(errno));
  531.     else
  532.     (void)execv(sv[0], sv);
  533. #else
  534.     (void)execv(sv[0], sv);
  535. #endif    /* defined(TRUSTED) */
  536.  
  537.     /* Something failed; dump the message and quit */
  538.     Fprintf(stderr, "%s:  Can't execv %s, %s.\n",
  539.     Pname, sv[0], strerror(errno));
  540.     while (fgets(buff, sizeof buff, stdin))
  541.     Fputs(buff, stdout);
  542.     exit(EX_OSERR);
  543.     /* NOTREACHED */
  544. }
  545.